'''
    FUDGE Game Engine
'''

from core import *
from models import Empty
from renderers import DefaultRenderer
from physics import Physics
import events
import ode

#import time

###
### Core Items
###
    

@singleton
class Engine():
    '''
        Engine, wraps and handles everything
    '''
    
    fullscreen = False
    scene_root = Empty()         # Root scene object
    width = 640
    height = 480
    
    @classmethod
    def __init__(self, argv = [], renderer = None, physics = None):
        # read in args
        for arg in argv:
            if arg[:2] == '-f':
                self.fullscreen = True
            if arg[:2] == '-r':
                width, x, height = arg[2:].partition('x')
                self.width, self.height = int(width), int(height)
                
        # initialize renderer if given
        if renderer:
            Renderer(renderer)
        # initialize timer
        self.time = None
    
    @classmethod
    def getWindow(self):
        # init window
        return Window('Fudge', height = self.height, width = self.width)
    
    @classmethod
    def Idle(self):
        pass
    
    @classmethod
    def Loop(self):
        ''' Main display loop '''
        if self.time is None:
            self.time = time.clock() - 0.01
        t1 = time.clock()
        dt = t1 - self.time
        self.time = t1
        
        # invoke render method
        Window()._on_Render(dt)
        
        # render and do physics
        Renderer().Render()
        Physics().Do(dt)
    
    @property
    def scene(self):
        return self.scene_root
        
    @classmethod
    def run(self):
        '''
            call this last
        '''
        # initialize core singletons
        Renderer().Initialize()
        Window('Fudge', height = self.height, width = self.width)
        Physics().Initialize()
        
               
        glutMainLoop()

@singleton
class Renderer():
    '''
        Singleton Renderer Manager. Needs to be initalized with a specific
        Renderer, chosen from the renderer package. Automatically initialized by Engine
    '''
    _renderEngine = None
    def __init__(self, renderEngine = renderers.DefaultRenderer()):
        self._renderEngine = renderEngine
    
    def Initialize(self):
        self._renderEngine.width = Engine().width
        self._renderEngine.height = Engine().height
        self._renderEngine.Initialize()
    
    def Render(self):
        self._renderEngine.Render(Engine())
        
    def get(self):
        return self._renderEngine
        
   



@singleton
class Window():
    '''
        Windows are content containers and handle input events. There can be at most one Window at a time.
    '''
    width = None
    height = None
    isDragging = None
    _renderer = None            # renderer instance [shortcut]
    
    @classmethod
    def __init__(self, title = 'Fudge', width = 640, height = 480):
        # dimensions
        self.width = width
        self.height = height
        
        # status flags
        self.isDragging = False
        
        # handlers
        self.handlers = {
            events.MouseClick: dict(),
            events.MouseMove: dict(),
            events.WindowResize: dict(),
            events.KeyPress: dict(),
            events.KeyUp: dict(),
            events.Render: dict(),
        }
        
        # intialize and create window
        glutInit()
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
        glutInitWindowSize(width, height)
        glutInitWindowPosition(0, 0)
        
        if Engine().fullscreen:
            glutGameModeString('%ix%i:%i' % (width, height, 32))
            if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)):
                glutEnterGameMode()
            else:
                print "The select mode is not available\n"
                exit(1)
        else:
            self.window_hdl = glutCreateWindow(title)
        
        
        
        
        self._registerHandlers()
        
    @classmethod
    def _registerHandlers(self):
        # Register the function called when our window is resized.
        glutReshapeFunc(self._on_WindowResize)
        # register keyboard function
        glutKeyboardFunc(self._on_KeyPress)
        # register key up functions
        glutKeyboardUpFunc(self._on_KeyUp)
        # mouse click function
        glutMouseFunc (self._on_MouseClick)
        # mouse move function
        glutMotionFunc (self._on_MouseMove)
        
        # engage renderer
        glutDisplayFunc(Engine().Loop)
        glutIdleFunc(Engine().Loop)
        
    @classmethod
    def _on_MouseMove(self, cursor_x, cursor_y):
        """ 
            Mouse Move generic handler
        """
        for name, hdl in self.handlers[events.MouseMove].items():
            hdl._fire(cursor_x = cursor_x, cursor_y = cursor_y)
            
    @classmethod
    def _on_MouseClick(self, button, button_state, cursor_x, cursor_y):
        '''
            Mouse Click generic handler
        '''
        for name, hdl in self.handlers[events.MouseClick].items():
            hdl._fire(button = button, button_state = button_state, cursor_x = cursor_x, cursor_y = cursor_y)

    @classmethod
    def _on_KeyUp(self, key, x, y):
        '''
        '''
        for name, hdl in self.handlers[events.KeyUp].items():
            hdl._fire(key = key)
    
    @classmethod
    def _on_KeyPress(self, key, x, y):
        '''
            Key Press generic handler
        '''
        for name, hdl in self.handlers[events.KeyPress].items():
            hdl._fire(key = key)

    @classmethod
    def _on_Render(self, dt):
        for name, hdl in self.handlers[events.Render].items():
            hdl._fire(timedelta = dt)
            
    @classmethod
    def _on_WindowResize(self, Width, Height):
        '''
            Reshape The Window When It's Moved Or Resized
        '''
        if Height == 0:						# Prevent A Divide By Zero If The Window Is Too Small 
            Height = 1

        glViewport(0, 0, Width, Height)		# Reset The Current Viewport And Perspective Transformation
        glMatrixMode(GL_PROJECTION)			# // Select The Projection Matrix
        glLoadIdentity()					# // Reset The Projection Matrix
        # field of view, aspect ratio, near and far
        gluPerspective(45.0, float(Width)/float(Height), 0.05, 60.0)

        glMatrixMode (GL_MODELVIEW);		# // Select The Modelview Matrix
        glLoadIdentity ()					# // Reset The Modelview Matrix
        
        for name, hdl in self.handlers[events.WindowResize].items():
            hdl._fire(width = Width, height = Height)
        return
            
    # add an event Handler
    def addEventHandler(self, ehdl):
        self.handlers[ehdl._Event_class][ehdl.name] = ehdl
        
    # add event handler function, suitable for use as decorator
    def add_event_handler(self, type, name="", state=""):
        def createHandler(fn):
            ehdl = events.Handler.Create(type, fn, name, state)
            self.addEventHandler(ehdl)
        return createHandler